#include "cnc_fanuc.h"

// 读取Fanuc数控系统的程序列表
int cnc_read_proglist_fanuc(const char* ip, unsigned short port, PRGDIR3* prg, short* num)
{
    unsigned short h;
    short ret;
    long top = 0;
    
    // 分配库句柄
    ret = cnc_allclibhndl3(ip, port, 3, &h);
    if (ret) {
        std::cerr << "Failed to allocate library handle, error code: " << ret << std::endl;
        return 1;
    }

    // 读取程序目录
    ret = cnc_rdprogdir3(h, 2, &top, num, prg);
    if (ret) {
        std::cerr << "Error reading program directory, error code: " << ret << std::endl;
        cnc_freelibhndl(h);
        return 1;
    }

    if (*num > 100) {
        std::cerr << "Program count exceeds maximum value. Program count: " << *num << std::endl;
        cnc_freelibhndl(h);
        return 1;
    }

    std::cout << "Program count: " << *num << std::endl;
    for (int i = 0; i < *num; ++i) {
        std::cout << "O" << prg[i].number 
                  << " " << prg[i].mdate.year << "-" << prg[i].mdate.month << "-" << prg[i].mdate.day
                  << " " << prg[i].mdate.hour << ":" << prg[i].mdate.minute 
                  << " " << prg[i].comment << std::endl;
    }

    cnc_freelibhndl(h);

    std::cout << "Program list read successfully." << std::endl;

    return 0;
}

// 从Fanuc数控系统下载程序
int cnc_download_prog_fanuc(const char* ip, unsigned short port, const char* directory, const char* filename, char** prog_buffer, size_t* buffer_size)
{
    unsigned short h;
    short ret;
    long len;
    char buf[1281];
    long prgnum = 0;

    // 从文件名中提取程序号(例如 O4001.bin -> 4001)
    size_t nemelen = strlen(filename);
    if (nemelen >= 6 && filename[0] == 'O' && strcmp(filename + nemelen - 4, ".bin") == 0) {
        char num_str[nemelen-4] = {0};
        strncpy(num_str, filename + 1, nemelen - 5);
        prgnum = strtol(num_str, NULL, 10);
    } else {
        std::cerr << "Invalid filename format, expected Oxxxx.bin: " << filename << std::endl;
        return 1;
    }

    // 分配库句柄
    ret = cnc_allclibhndl3(ip, port, 3, &h);
    if (ret) {
        std::cerr << "Failed to allocate library handle, error code: " << ret << std::endl;
        return 1;
    }

    // 下载程序开始
    ret = cnc_upstart3(h, 0, prgnum, prgnum);
    if (ret) {
        std::cerr << "Failed to start program, program number: " << prgnum << ", error code: " << ret << std::endl;
        cnc_freelibhndl(h);
        return 1;
    }

    // 初始化程序缓冲区
    char* local_prog_buffer = nullptr;
    size_t local_buffer_size = 0;
    
    // 下载程序
    do {
        len = 1280;
        ret = cnc_upload3(h, &len, buf);
        if (ret == EW_BUFFER) {
            continue;
        } 
        if (ret == EW_OK) {
            buf[len] = '\0';
            
            // 追加内容到缓冲区
            char* new_buffer = (char*)realloc(local_prog_buffer, local_buffer_size + len + 1);
            if (!new_buffer) {
                std::cerr << "Memory allocation failed" << std::endl;
                free(local_prog_buffer);
                cnc_upend3(h);
                cnc_freelibhndl(h);
                return 1;
            }
            local_prog_buffer = new_buffer;
            memcpy(local_prog_buffer + local_buffer_size, buf, len);
            local_buffer_size += len;
            local_prog_buffer[local_buffer_size] = '\0';
        }
        if (buf[len-1] == '%') {
            break;
        }
    } while ((ret == EW_OK) || (ret == EW_BUFFER));

    if (ret) {
        std::cerr << "Failed to download program, program number: " << prgnum << ", error code: " << ret << std::endl;
        free(prog_buffer);
        cnc_upend3(h);
        cnc_freelibhndl(h);
        return 1;
    }
    
    cnc_upend3(h);
    cnc_freelibhndl(h);

    // 拼接路径名
    std::string fullpath = directory;
    fullpath += filename;

    // 写入缓冲区到文件
    std::ofstream outfile(fullpath, std::ios::binary);
    if (!outfile) {
        std::cerr << "Failed to open file for writing: " << fullpath << ". Error: " << strerror(errno) << std::endl;
        free(prog_buffer);
        return 1;
    }
    
    outfile.write(local_prog_buffer, local_buffer_size);
    if (!outfile) {
        std::cerr << "Failed to write to file: " << fullpath << ". Error: " << strerror(errno) << std::endl;
        if (!prog_buffer) {
            free(local_prog_buffer);
        }
        return 1;
    }

    if (prog_buffer) {
        *prog_buffer = local_prog_buffer;
        if (buffer_size) {
            *buffer_size = local_buffer_size;
        }
    } else {
        free(local_prog_buffer);
    }

    std::cout << "Program downloaded successfully." << std::endl;

    return 0;
}

// 从Fanuc数控系统上传程序
int cnc_upload_prog_fanuc(const char* ip, unsigned short port, const char* directory, const char* filename)
{
    unsigned short h;
    short ret;
    long len, n;
    char *prg;

    // 拼接完整文件路径
    std::string fullpath = directory;
    fullpath += filename;

    // 读取文件内容
    std::ifstream infile(fullpath, std::ios::binary | std::ios::ate);
    if (!infile) {
        std::cerr << "Failed to open file for reading: " << fullpath << ". Error: " << strerror(errno) << std::endl;
        return 1;
    }

    // 获取文件大小并分配内存
    len = infile.tellg();
    if (len <= 0) {
        std::cerr << "Invalid file size: " << len << std::endl;
        return 1;
    }
    
    char* orig_prg = new char[len + 1];
    prg = orig_prg;
    infile.seekg(0);
    infile.read(prg, len);
    prg[len] = '\0';
    infile.close();

    // 分配库句柄
    ret = cnc_allclibhndl3(ip, port, 3, &h);
    if (ret) {
        std::cerr << "Failed to allocate library handle, error code: " << ret << std::endl;
        return 1;
    }

    // 上传程序开始
    ret = cnc_dwnstart3(h, 0);
    if (ret) {
        std::cerr << "Failed to start program, error code: " << ret << std::endl;
        cnc_freelibhndl(h);
        return 1;
    }

    // 上传程序
    while (len > 0) {
        n = len;
        ret = cnc_download3(h, &n, prg);
        if (ret == EW_BUFFER) {
            continue;
        } else {
            break;
        }
    }

    if (ret) {
        std::cerr << "Failed to upload program, error code: " << ret << std::endl;
        cnc_upend3(h);
        cnc_freelibhndl(h);
        return 1;
    }

    cnc_dwnend3(h);
    cnc_freelibhndl(h);
    
    delete[] orig_prg;

    std::cout << "Program uploaded successfully." << std::endl;

    return 0;
}

// 删除Fanuc数控系统的程序
int cnc_delete_prog_fanuc(const char* ip, unsigned short port, const char* filename)
{
    unsigned short h;
    short ret;
    long prgnum = 0;

    // 从文件名中提取程序号(例如 O4001.bin -> 4001)
    size_t nemelen = strlen(filename);
    if (nemelen >= 6 && filename[0] == 'O' && strcmp(filename + nemelen - 4, ".bin") == 0) {
        char num_str[nemelen-4] = {0};
        strncpy(num_str, filename + 1, nemelen - 5);
        prgnum = strtol(num_str, NULL, 10);
    } else {
        std::cerr << "Invalid filename format, expected Oxxxx.bin: " << filename << std::endl;
        return 1;
    }

    // 分配库句柄
    ret = cnc_allclibhndl3(ip, port, 3, &h);
    if (ret) {
        std::cerr << "Failed to allocate library handle, error code: " << ret << std::endl;
        return 1;
    }

    // 删除程序
    ret = cnc_delete(h, prgnum);
    if (ret) {
        std::cerr << "Failed to delete program, program number: " << prgnum << ", error code: " << ret << std::endl;
        cnc_freelibhndl(h);
        return 1;
    }

    cnc_freelibhndl(h);

    std::cout << "Program " << prgnum << " deleted successfully." << std::endl;

    return 0;
}
